package com.example.es.template;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Cancellable;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Component
public class ElasticsearchTemplate {

    private final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchTemplate.class);

    private final RestHighLevelClient client;

    public ElasticsearchTemplate(@Qualifier("restHighLevelClient") RestHighLevelClient client) {
        this.client = client;
    }

    /**
     * 创建索引
     *
     * @param indexRequest
     * @return
     * @author liufeng
     * @date 2020年3月1日
     */
    public boolean createIndex(CreateIndexRequest indexRequest) throws IOException {
        final String index = indexRequest.index();
        if (isIndexExist(index)) {
            LOGGER.info("Index exits! {}", index);
            return false;
        }
        final CreateIndexResponse indexresponse = client.indices().create(indexRequest, RequestOptions.DEFAULT);
        LOGGER.info("执行建立索引：{}，成功？:{}", index, indexresponse.isAcknowledged());
        return indexresponse.isAcknowledged();
    }

    /**
     * 删除索引
     *
     * @param index
     * @return
     */
    public boolean deleteIndex(String index) throws IOException {
        if (!isIndexExist(index)) {
            LOGGER.info("Index not exits! {}", index);
            return false;
        }
        final boolean acknowledged = client.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT).isAcknowledged();
        if (acknowledged) {
            LOGGER.info("delete index {} successfully!", index);
        } else {
            LOGGER.info("Fail to delete index {}", index);
        }
        return acknowledged;
    }

    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     */
    public boolean isIndexExist(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest(index);
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 数据添加，正定ID
     *
     * @param source  要增加的数据
     * @param index   索引，类似数据库
     * @param id      数据ID
     * @return
     */
    public IndexResponse addData(String index, String id, Map<String, ?> source) throws IOException {
        final IndexRequest indexRequest = new IndexRequest(index).id(id).source(source);
        return client.index(indexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 异步数据添加，正定ID
     *
     * @param source  要增加的数据
     * @param index   索引，类似数据库
     * @param id      数据ID
     * @return
     */
    public Cancellable asyncAddData(String index, String id, Map<String, ?> source) throws IOException {
        final IndexRequest indexRequest = new IndexRequest(index).id(id).source(source);
        return client.indexAsync(indexRequest, RequestOptions.DEFAULT, null);
    }

    /**
     * 数据添加
     *
     * @param source 要增加的数据
     * @param index  索引，类似数据库
     * @return
     */
    public IndexResponse addData(Map<String, ?> source, String index) throws IOException {
        return addData(index, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(),source);
    }

    public BulkResponse batchIndex(String index, List<Map<String,Object>> bulks) throws IOException {
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest request = null;
            for(Map<String,Object> bulk: bulks) {
                request = new IndexRequest();
                request.index(index).id(bulk.getOrDefault("id",UUID.randomUUID()).toString())
                        .source(bulk);
                bulkRequest.add(request);
            }
            return client.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    public Cancellable batchAyncIndex(String index, List<Map<String,Object>> bulks,ActionListener<BulkResponse> listener) throws IOException {
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest request = null;
            for(Map<String,Object> bulk: bulks) {
                request = new IndexRequest();
                request.index(index).id(bulk.getOrDefault("id",UUID.randomUUID()).toString())
                        .source(bulk);
                bulkRequest.add(request);
            }
            return client.bulkAsync(bulkRequest, RequestOptions.DEFAULT,listener);
    }

    /**
     * 通过ID删除数据
     *
     * @param index 索引，类似数据库
     * @param id    数据ID
     */
    public DeleteResponse deleteById(String index, String id) throws IOException {
        return client.delete(new DeleteRequest(index).id(id),RequestOptions.DEFAULT);
    }

    /**
     * 通过ID删除数据
     *
     * @param index 索引，类似数据库
     */
    public BulkResponse batchDeleteById(String index, List<String> ids) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        DeleteRequest request = null;
        for(String id: ids) {
            request = new DeleteRequest();
            request.index(index).id(id);
            bulkRequest.add(request);
        }
        return client.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    /**
     * 通过ID 更新数据
     *
     * @param source 要增加的数据
     * @param index      索引，类似数据库
     * @param id         数据ID
     * @return
     */
    public UpdateResponse updateById(Map<String, ?> source, String index, String id) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index(index).id(id).doc(source);
        return client.update(updateRequest,RequestOptions.DEFAULT);
    }

    /**
     * 通过ID获取数据
     *
     * @param index  索引，类似数据库
     * @param id     数据ID
     * @param fields 需要显示的字段，逗号分隔（缺省为全部字段）
     * @return
     */
    public SearchHits searchById(String index, String id, String fields) throws IOException {
        final SearchHits hits = client.search(new SearchRequest(index).preference(fields), RequestOptions.DEFAULT).getHits();
        return hits;
    }

    /**
     * 使用分词查询,并分页
     *
     * @param index          索引名称
     * @param currentPage    当前页
     * @param pageSize       每页显示条数
     * @param fields         需要显示的字段，逗号分隔（缺省为全部字段）
     * @param sorts          排序字段
     * @param matchPhrase    true 使用，短语精准匹配
     * @param highlightField 高亮字段
     * @param matchStr       过滤条件（xxx=111,aaa=222）
     * @return
     */
    public SearchResponse searchDataPage(String index, int currentPage, int pageSize, String[] fields, List<SortBuilder<?>> sorts, boolean matchPhrase, String highlightField, String matchStr) throws IOException {
        SearchSourceBuilder searchRequestBuilder = new SearchSourceBuilder();
        // 需要显示的字段，逗号分隔（缺省为全部字段）
        if (fields != null) {
            searchRequestBuilder.fetchSource(fields, null);
        }

        //排序字段
        if (!CollectionUtils.isEmpty(sorts)) {
            searchRequestBuilder.sorts().addAll(sorts);
        }

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 查询字段
        if (!StringUtils.isEmpty(matchStr)) {
            for (String s : matchStr.split(",")) {
                String[] ss = s.split("=");
                if (matchPhrase == Boolean.TRUE) {
                    boolQuery.must(QueryBuilders.matchPhraseQuery(s.split("=")[0], s.split("=")[1]));
                } else {
                    boolQuery.must(QueryBuilders.matchQuery(s.split("=")[0], s.split("=")[1]));
                }
            }
        }

        // 高亮（xxx=111,aaa=222）
        if (!StringUtils.isEmpty(highlightField)) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.preTags("<span style='color:red' >");//设置前缀
            highlightBuilder.postTags("</span>");//设置后缀
            // 设置高亮字段
            highlightBuilder.field(highlightField);
            searchRequestBuilder.highlighter(highlightBuilder);
        }
        searchRequestBuilder.query(boolQuery);

        // 分页应用
        searchRequestBuilder.from(currentPage).size(pageSize);

        // 设置是否按查询匹配度排序
        searchRequestBuilder.explain(true);
        return search(searchRequestBuilder,index);
    }

    /**
     * 高亮结果集 特殊处理
     *
     * @param searchResponse
     * @param highlightField
     */
    private List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) {
        List<Map<String, Object>> sourceList = new ArrayList<Map<String, Object>>();
        StringBuffer stringBuffer = new StringBuffer();

        for (SearchHit searchHit : searchResponse.getHits().getHits()) {
            searchHit.getSourceAsMap().put("id", searchHit.getId());

            if (!StringUtils.isEmpty(highlightField)) {
                final Text[] texts = searchHit.getHighlightFields().get(highlightField).getFragments();

                if (texts != null) {
                    for (Text str : texts) {
                        stringBuffer.append(str.string());
                    }
                    //遍历 高亮结果集，覆盖 正常结果集
                    searchHit.getSourceAsMap().put(highlightField, stringBuffer.toString());
                }
            }
            sourceList.add(searchHit.getSourceAsMap());
        }
        return sourceList;
    }

    public SearchResponse search(SearchSourceBuilder sourceBuilder,String... indices) throws IOException {
        SearchRequest searchRequest = new SearchRequest(indices);
        searchRequest.source(sourceBuilder);
        return client.search(searchRequest, RequestOptions.DEFAULT);
    }

}